<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="utf-8" />
  <title>ECharts 关系图：动态展开收起 + 按钮添加数据</title>
  <style>
    html, body, #main { height: 100%; margin: 0; }
    #controls {
      position: absolute;
      top: 10px;
      left: 10px;
      z-index: 10;
      background: rgba(255,255,255,0.9);
      padding: 6px 10px;
      border-radius: 4px;
      font-family: sans-serif;
    }
    #controls input {
      width: 120px;
    }
  </style>
</head>
<body>
<div id="controls">
  <input id="targetNode" list="nodeList" placeholder="选择或输入节点ID" value="root">
  <datalist id="nodeList"></datalist>
  <button id="addDataBtn">添加子节点</button>
</div>

<div id="main"></div>

<script src="./charts.min.js"></script>
<script>
const chart = echarts.init(document.getElementById('main'));

// 数据存储（不删除节点，只控制 visible）
const store = {
  nodes: [
    { id: 'root', name: '根节点', x: 0, y: 0, visible: true, loaded: false, parentId: null }
  ],
  links: []
};

function updateNodeList() {
  const listEl = document.getElementById('nodeList');
  listEl.innerHTML = store.nodes
    .map(n => `<option value="${n.id}">${n.name}</option>`)
    .join('');
}

// 切换根节点，重置 store 并渲染
function switchRootNode(newRootId) {
  const node = findNode(newRootId);
  if (!node) {
    alert('节点不存在');
    return;
  }
  // 重置 store，只保留新根节点，且标记为未加载
  store.nodes = [{
    id: node.id,
    name: node.name,
    x: 0,
    y: 0,
    visible: true,
    loaded: false,
    parentId: null,
    info: node.info || ''
  }];
  store.links = [];

  document.getElementById('targetNode').value = newRootId;

  render();
}

// 输入框监听选中事件（change事件也能兼容手动输入）
document.getElementById('targetNode').addEventListener('change', (e) => {
  const newRootId = e.target.value.trim();
  if (newRootId && newRootId !== store.nodes[0].id) {
    switchRootNode(newRootId);
  }
});

function findNode(id) {
  return store.nodes.find(n => n.id === id);
}

// 模拟异步加载子节点
function loadChildren(parentId, forceAdd = false) {
  return new Promise(resolve => {
    setTimeout(() => {
      const parent = findNode(parentId);
      if (!parent) return resolve();
      const newNodes = [];
      const newLinks = [];
      const count = 2 + Math.floor(Math.random() * 3); // 2~4个子节点
      for (let i = 0; i < count; i++) {
        const id = `${parentId}-${Date.now()}-${i}`; // 保证唯一
        newNodes.push({
          id,
          name: '节点 ' + id,
          x: parent.x + Math.random() * 200 - 100,
          y: parent.y + Math.random() * 200 - 100,
          visible: true,
          loaded: false,
          parentId,
        });
        newLinks.push({ source: parentId, target: id });
      }
      store.nodes.push(...newNodes);
      store.links.push(...newLinks);
      if (!forceAdd) parent.loaded = true; // 正常展开加载
      resolve();
    }, 300); // 模拟0.3秒网络延迟
  });
}

// 收起：递归隐藏所有子孙
function hideSubtree(id) {
  const children = store.nodes.filter(n => n.parentId === id);
  for (const child of children) {
    child.visible = false;
    hideSubtree(child.id);
  }
}

// 收起：只隐藏一层（用于根节点）
function hideOneLevel(id) {
  const children = store.nodes.filter(n => n.parentId === id);
  for (const child of children) {
    child.visible = false;
  }
}

function render() {
  const visibleNodes = store.nodes.filter(n => n.visible);
  const visibleNodeIds = new Set(visibleNodes.map(n => n.id));
  const visibleLinks = store.links.filter(l => visibleNodeIds.has(l.source) && visibleNodeIds.has(l.target));

  chart.setOption({
    series: [{
      type: 'graph',
      layout: 'force',
      roam: true,
      draggable: true,
      label: { show: true },
      force: {
        repulsion: 120,
        edgeLength: 100
      },
      data: visibleNodes,
      links: visibleLinks,
	  emphasis: {
		  focus: 'adjacency', // 高亮当前节点及相关连线、相邻节点
		  label: { show: true } // 悬停时显示标签
		},
		lineStyle: {
		  opacity: 0.6
		},
		blur: {
		  lineStyle: { opacity: 0.1 }, // 非关联边变淡
		  itemStyle: { opacity: 0.1 }  // 非关联节点变淡
		}

    }]
  });
  updateNodeList(); // 每次渲染后更新节点选择列表
}

// 单击：只负责收起
chart.on('click', params => {
  if (params.dataType === 'node') {
    const node = findNode(params.data.id);
    const directChildren = store.nodes.filter(n => n.parentId === node.id);
    const hasVisibleChild = directChildren.some(c => c.visible);

    if (hasVisibleChild) {
      if (node.id === 'root') {
        hideOneLevel(node.id);
      } else {
        hideSubtree(node.id);
      }
      render();
    }
  }
});

// 双击：负责展开 / 加载
chart.on('dblclick', async params => {
  if (params.dataType === 'node') {
    const node = findNode(params.data.id);
    const directChildren = store.nodes.filter(n => n.parentId === node.id);

    // 如果已经有可见的子节点就不重复加载
    if (directChildren.some(c => c.visible)) return;

    if (!node.loaded) {
      await loadChildren(node.id);
    } else {
      directChildren.forEach(c => c.visible = true);
    }
    render();
  }
});

// 更新tooltip内容的简单函数（调用一次render即可）
function updateTooltip() {
  // 注意这里只需要更新tooltip相关即可，可用setOption合并更新
  chart.setOption({
    tooltip: {
      formatter: function (params) {
        if (params.dataType === 'node') {
          return `<b>${params.data.name}</b><br/>${params.data.info || '暂无详细信息'}`;
        } else if (params.dataType === 'edge') {
          return `${params.data.source} → ${params.data.target}`;
        }
      }
    }
  });
}
const infoCache = {};  // 缓存已请求过的节点详情
chart.on('mouseover', async params => {
  if (params.dataType === 'node') {
    const nodeId = params.data.id;

    if (infoCache[nodeId]) {
      // 已缓存，更新 tooltip 数据即可
      const node = findNode(nodeId);
      node.info = infoCache[nodeId];
      updateTooltip();
    } else {
      try {
        // 模拟API调用
        // const response = await fetch(`/api/nodeInfo?id=${encodeURIComponent(nodeId)}`);
        // if (!response.ok) throw new Error('网络错误');
        // const data = await response.json();
        // 假设接口返回 { info: '详细信息' }
        // infoCache[nodeId] = data.info || '无详细信息';
        infoCache[nodeId] = '无详细信息';

        const node = findNode(nodeId);
        if (node) {
          node.info = infoCache[nodeId];
          updateTooltip();
        }
      } catch (e) {
        console.error('获取节点详情失败', e);
      }
    }
  }
});

// 按钮：动态往指定节点添加数据
document.getElementById('addDataBtn').onclick = async () => {
  const targetId = document.getElementById('targetNode').value.trim();
  if (!targetId) return alert('请输入节点ID');
  if (!findNode(targetId)) return alert('节点不存在');

  await loadChildren(targetId, true); // 强制添加，不改 loaded 状态
  render();
};

// 初始渲染
render();
</script>
</body>
</html>
